/*
 * Copyright (C) 2012-2013  Romain DUBOIS
 * 
 * This file is part of NeMoS - Network and Modular Software
 * 
 * NeMoS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * NeMoS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with NeMoS.  If not, see <http://www.gnu.org/licenses/>.
 */
package nemos.util.swing;

import java.util.ArrayList;
import java.util.Collection;

import javax.swing.AbstractListModel;
import javax.swing.event.ListDataListener;

/**
 * ArrayListModel : The {@link ArrayList}-based list model.
 * 
 * @author Romain DUBOIS
 * @param <T>
 *            The elements type
 */
public class ArrayListModel<T> extends ArrayList<T> implements IListModel<T> {

    /** Serial version */
    private static final long serialVersionUID = 1L;

    /** The delegate list model */
    private final DelegateModel mDelegate;

    /**
     * Constructor.
     */
    public ArrayListModel() {
        super();

        mDelegate = new DelegateModel();
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.util.List#add(java.lang.Object)
     */
    @Override
    public boolean add(final T pE) {
        final int lSize = super.size();
        final boolean lAdded = super.add(pE);
        if (lAdded) {
            mDelegate.fireIntervalAdded(this, lSize, lSize);
        }
        return lAdded;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.util.List#remove(java.lang.Object)
     */
    @Override
    public boolean remove(final Object pO) {
        final int lIndex = super.indexOf(pO);
        final boolean lRemoved = super.remove(pO);
        if (lRemoved) {
            mDelegate.fireIntervalRemoved(this, lIndex, lIndex);
        }
        return lRemoved;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.util.List#addAll(java.util.Collection)
     */
    @Override
    public boolean addAll(final Collection<? extends T> pC) {
        final int lSize = super.size();
        final boolean lAdded = super.addAll(pC);
        if (lAdded) {
            mDelegate.fireIntervalAdded(this, lSize, lSize + pC.size());
        }
        return lAdded;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.util.List#addAll(int, java.util.Collection)
     */
    @Override
    public boolean addAll(final int pIndex, final Collection<? extends T> pC) {
        final boolean lAdded = super.addAll(pIndex, pC);
        if (lAdded) {
            mDelegate.fireIntervalAdded(this, pIndex, pIndex + pC.size());
        }
        return lAdded;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.util.List#removeAll(java.util.Collection)
     */
    @Override
    public boolean removeAll(final Collection<?> pC) {
        final int lEnd = super.size() - 1;
        final boolean lRemoved = super.removeAll(pC);
        if (lRemoved) {
            mDelegate.fireContentsChanged(this, 0, lEnd);
        }
        return lRemoved;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.util.List#retainAll(java.util.Collection)
     */
    @Override
    public boolean retainAll(final Collection<?> pC) {
        final int lEnd = super.size() - 1;
        final boolean lRemoved = super.retainAll(pC);
        if (lRemoved) {
            mDelegate.fireContentsChanged(this, 0, lEnd);
        }
        return lRemoved;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.util.List#clear()
     */
    @Override
    public void clear() {
        final int lSize = super.size() - 1;
        super.clear();
        if (lSize >= 0) {
            mDelegate.fireIntervalRemoved(this, 0, lSize);
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.util.List#set(int, java.lang.Object)
     */
    @Override
    public T set(final int pIndex, final T pElement) {
        final T lResult = super.set(pIndex, pElement);
        mDelegate.fireContentsChanged(this, pIndex, pIndex);
        return lResult;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.util.List#add(int, java.lang.Object)
     */
    @Override
    public void add(final int pIndex, final T pElement) {
        super.add(pIndex, pElement);
        mDelegate.fireIntervalAdded(this, pIndex, pIndex);
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.util.List#remove(int)
     */
    @Override
    public T remove(final int pIndex) {
        final T lResult = super.remove(pIndex);
        mDelegate.fireIntervalRemoved(this, pIndex, pIndex);
        return lResult;
    }

    /**
     * {@inheritDoc}
     * 
     * @see javax.swing.ListModel#getSize()
     */
    @Override
    public int getSize() {
        return size();
    }

    /**
     * {@inheritDoc}
     * 
     * @see javax.swing.ListModel#addListDataListener(javax.swing.event.ListDataListener)
     */
    @Override
    public void addListDataListener(final ListDataListener pL) {
        mDelegate.addListDataListener(pL);
    }

    /**
     * {@inheritDoc}
     * 
     * @see javax.swing.ListModel#removeListDataListener(javax.swing.event.ListDataListener)
     */
    @Override
    public void removeListDataListener(final ListDataListener pL) {
        mDelegate.removeListDataListener(pL);
    }

    /**
     * {@inheritDoc}
     * 
     * @see nemos.util.swing.IListModel#getElementAt(int)
     */
    @Override
    public T getElementAt(final int pIndex) {
        return get(pIndex);
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.util.ArrayList#get(int)
     */
    @Override
    public T get(final int pIndex) {
        if (size() > pIndex) {
            return super.get(pIndex);
        }
        return null;
    }

    /**
     * DelegateModel : The inner delegate model.
     * 
     * <p>
     * This class is used to fire list model events.
     * </p>
     * 
     * @author Romain DUBOIS
     */
    class DelegateModel extends AbstractListModel {

        /** Serial version */
        private static final long serialVersionUID = 1L;

        /**
         * {@inheritDoc}
         * 
         * @see javax.swing.ListModel#getSize()
         */
        @Override
        public int getSize() {
            return ArrayListModel.this.size();
        }

        /**
         * {@inheritDoc}
         * 
         * @see javax.swing.ListModel#getElementAt(int)
         */
        @Override
        public Object getElementAt(final int pIndex) {
            return ArrayListModel.this.get(pIndex);
        }

        /**
         * {@inheritDoc}
         * 
         * @see javax.swing.AbstractListModel#fireContentsChanged(java.lang.Object,
         *      int, int)
         */
        @Override
        protected void fireContentsChanged(final Object pSource, final int pIndex0,
                final int pIndex1) {
            super.fireContentsChanged(pSource, pIndex0, pIndex1);
        }

        /**
         * {@inheritDoc}
         * 
         * @see javax.swing.AbstractListModel#fireIntervalAdded(java.lang.Object,
         *      int, int)
         */
        @Override
        protected void fireIntervalAdded(final Object pSource, final int pIndex0, final int pIndex1) {
            super.fireIntervalAdded(pSource, pIndex0, pIndex1);
        }

        /**
         * {@inheritDoc}
         * 
         * @see javax.swing.AbstractListModel#fireIntervalRemoved(java.lang.Object,
         *      int, int)
         */
        @Override
        protected void fireIntervalRemoved(final Object pSource, final int pIndex0,
                final int pIndex1) {
            super.fireIntervalRemoved(pSource, pIndex0, pIndex1);
        }
    }
}
